﻿using System;
using System.Collections.Generic;
using System.IO;

using DigitalQuran.Collection;
using DigitalQuran.Data.Metadata;

namespace DigitalQuran.IO
{
    /// <summary>
    /// Private IO Operations
    /// </summary>
    internal static class PrivateStorage
    {
        #region Qur'an Objects from Metadata
        /* ================= STRUCTURE OF METADATA FILE =================
         * 1. Chapters Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      - Total Bowings (Int32)
         *      - Arabic Name (String)
         *      - English Name (String)
         *      - Transliterated Name (String)
         *      - Revelation Place (Byte)
         *      - Revelation Order (Int32)    
         *      
         * 2. Part Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      - Arabic Name (String)
         *      - Transliterated Name (String)
         *      
         * 3. Group Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      
         * 4. Quarter Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      
         * 5. Station Metadats
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      
         * 6. Bowing Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      
         * 7. Page Metadata
         *      - Start Verse (Int32)
         *      - Total Verses (Int32)
         *      
         * 8. Prostration Metadata
         *      - Verse (Int32)
         *      - Type (byte)
         */

        /// <summary>
        /// Reads the Metadata file and Load Qur'an Objects saved in metadata
        /// </summary>
        internal static void LoadQuranObjectsFromMetadata()
        {
            using (var stream = new FileStream(DigitalQuranDirectories.Data + ".metadata", FileMode.Open))
            using (var reader = new BinaryReader(stream))
            {
                /* 1 */ ChapterCollection.LoadData(EnumerateChapters(reader));
                /* 2 */ PartCollection.LoadData(EnumerateParts(reader));
                /* 3 */ GroupCollection.LoadData(EnumerateValueSpan(reader, GroupCollection.TotalGroups));
                /* 4 */ QuarterCollection.LoadData(EnumerateValueSpan(reader, QuarterCollection.TotalQuarters));
                /* 5 */ StationCollection.LoadData(EnumerateValueSpan(reader, StationCollection.TotalStations));
                /* 6 */ BowingCollection.LoadData(EnumerateValueSpan(reader, BowingCollection.TotalBowings));
                /* 7 */ PageCollection.LoadData(EnumerateValueSpan(reader, PageCollection.TotalPages));
                /* 8 */ ProstrationCollection.LoadData(EnumerateProstration(reader));
            }
        }

        static IEnumerator<ChapterMeta> EnumerateChapters(BinaryReader reader)
        {
            for (int i = 0; i < ChapterCollection.TotalChapters; i++)
            {
                /* ======== ITEM SEQUENCE =========
                 *      - Start Verse (Int32)
                 *      - Total Verses (Int32)
                 *      - Total Bowings (Int32)
                 *      - Arabic Name (String)
                 *      - English Name (String)
                 *      - Transliterated Name (String)
                 *      - Revelation Place (Byte)
                 *      - Revelation Order (Int32)    
                 */
                yield return new ChapterMeta()
                {
                    StartVerse = reader.ReadInt32(),
                    VerseCount = reader.ReadInt32(),
                    BowingCount = reader.ReadInt32(),
                    Name = reader.ReadString(),
                    EnglishName = reader.ReadString(),
                    TransliteratedName = reader.ReadString(),
                    RevelationPlace = (RevelationPlace)reader.ReadByte(),
                    RevelationOrder = reader.ReadInt32()
                };
            }
        }

        static IEnumerator<PartMeta> EnumerateParts(BinaryReader reader)
        {
            for (int i = 0; i < PartCollection.TotalParts; i++)
            {
                /* ===== ITEMS SEQUENCE =======
                 *      - Start Verse (Int32)
                 *      - Total Verses (Int32)
                 *      - Arabic Name (String)
                 *      - Transliterated Name (String)
                 */
                yield return new PartMeta()
                {
                    StartVerse = reader.ReadInt32(),
                    VerseCount = reader.ReadInt32(),
                    ArabicName = reader.ReadString(),
                    TransliteratedName = reader.ReadString()
                };
            }
        }

        static IEnumerator<ValueSpan> EnumerateValueSpan(BinaryReader reader, int count)
        {
            for (int i = 0; i < count; i++)
            {
                /* ===== ITEMS SEQUENCE =======
                 *      - Start Verse (Int32)
                 *      - Total Verses (Int32)
                 */
                yield return new ValueSpan
                {
                    LowerBound = reader.ReadInt32(),
                    Count = reader.ReadInt32()
                };
            }
        }

        static IEnumerator<ProstrationMeta> EnumerateProstration(BinaryReader reader)
        {
            for (int i = 0; i < PartCollection.TotalParts; i++)
            {
                /* ==== ITEMS SEQUENCE ====
                 *  - Verse (Int32)
                 *  - Type (byte)
                 */
                yield return new ProstrationMeta()
                {
                    VerseNo = reader.ReadInt32(),
                    Type = (ProstrationType)reader.ReadByte()
                };
            }
        }

        #endregion

        internal static IEnumerator<string[]> ReadVerses(string fileName)
        {
            /* ITEM SEQUENCE
             *  - Number of Words (Int32)
             *  - Seq of Words (String)
             */
            using (var stream = new FileStream(fileName, FileMode.Open))
            using (var reader = new BinaryReader(stream))
            {
                for (int i = 0; i < VerseCollection.TotalVerses; i++)
                {
                    var retValue = new string[reader.ReadInt32()];
                    for (int j = 0; j < retValue.Length; j++)
                        retValue[j] = reader.ReadString();

                    yield return retValue;
                }                
            }
        }
    }
}